1 module hunt.templates.parser; 2 3 import std.string; 4 import std.file; 5 import std.path; 6 import std.conv; 7 import std.stdio; 8 9 import hunt.templates.rule; 10 import hunt.templates.element; 11 import hunt.templates.match; 12 import hunt.templates.ast; 13 import hunt.templates.util; 14 import hunt.templates.cache; 15 16 class Parser 17 { 18 public: 19 ElementNotation element_notation = ElementNotation.Pointer; 20 21 this() 22 { 23 } 24 25 ElementExpression parse_expression(const string input) 26 { 27 28 if (input.length <= 0) 29 return new ElementExpression(Function.ReadJson); 30 auto match_function = RegexObj.match!Function(input, regex_map_functions); 31 switch (match_function.type()) 32 { 33 case Function.ReadJson: 34 { 35 string command = match_function.str(1); 36 37 if ((startsWith(command, '"') && endsWith(command, '"')) 38 || (startsWith(command, '\'') && endsWith(command, '\''))) 39 { // Result 40 ElementExpression result = new ElementExpression(Function.Result); 41 result.result = command[1 .. $ - 1]; 42 return result; 43 } 44 45 ElementExpression result = new ElementExpression(Function.ReadJson); 46 switch (element_notation) 47 { 48 case ElementNotation.Pointer: 49 { 50 //if (command[0] != '/') { command = "/"~command; } 51 result.command = command; 52 break; 53 } 54 case ElementNotation.Dot: 55 { 56 result.command = command; 57 break; 58 } 59 default: 60 template_engine_throw("parser_error", 61 "element notation: " ~ element_notation.stringof); 62 break; 63 } 64 return result; 65 } 66 default: 67 { 68 ElementExpression[] args; 69 for (int i = 1; i < match_function.size(); i++) 70 { // str(0) is whole group 71 args ~= parse_expression(match_function.str(i)); 72 } 73 74 ElementExpression result = new ElementExpression(match_function.type()); 75 result.args = args; 76 return result; 77 } 78 } 79 } 80 81 Element[] parse_level(string input, string path) 82 { 83 Element[] result; 84 85 size_t current_position = 0; 86 auto match_delimiter = RegexObj.search_all(input, current_position); 87 //writeln("-----3------"); 88 while (match_delimiter.found()) 89 { 90 current_position = match_delimiter.end_position(); 91 //writeln("---whole --- :",match_delimiter.str()); 92 string string_prefix = match_delimiter.prefix(); 93 if (!string_prefix.empty()) 94 { 95 result ~= new ElementString(string_prefix); 96 } 97 98 string delimiter_inner = match_delimiter.str(1); 99 100 switch (match_delimiter.type()) 101 { 102 case Delimiter.Statement: 103 case Delimiter.LineStatement: 104 { 105 106 auto match_statement = RegexObj.match!Statement(delimiter_inner, 107 regex_map_statement_openers); 108 switch (match_statement.type()) 109 { 110 case Statement.Loop: 111 { 112 MatchClosed loop_match = RegexObj.search_closed(input, 113 match_delimiter.pattern(), regex_map_statement_openers[Statement.Loop], 114 regex_map_statement_closers[Statement.Loop], match_delimiter); 115 116 current_position = loop_match.end_position(); 117 118 string loop_inner = match_statement.str(0); 119 auto match_command = RegexObj.match!Loop(loop_inner, regex_map_loop); 120 if (!match_command.found()) 121 { 122 template_engine_throw("parser_error", 123 "unknown loop statement: " ~ loop_inner); 124 } 125 //writeln("#############match type :",match_command.type()); 126 switch (match_command.type()) 127 { 128 case Loop.ForListIn: 129 { 130 string value_name = match_command.str(1); 131 string list_name = match_command.str(2); 132 result ~= new ElementLoop(match_command.type(), value_name, 133 parse_expression(list_name), loop_match.inner()); 134 break; 135 } 136 case Loop.ForMapIn: 137 { 138 string key_name = match_command.str(1); 139 string value_name = match_command.str(2); 140 string list_name = match_command.str(3); 141 142 result ~= new ElementLoop(match_command.type(), key_name, value_name, 143 parse_expression(list_name), loop_match.inner()); 144 break; 145 } 146 default: 147 template_engine_throw("parser_error", 148 "unknown loop statement: " ~ match_command.str()); 149 break; 150 } 151 break; 152 } 153 case Statement.Condition: 154 { 155 auto condition_container = new ElementConditionContainer(); 156 157 Match condition_match = match_delimiter; 158 MatchClosed else_if_match = RegexObj.search_closed_on_level(input, 159 match_delimiter.pattern(), 160 regex_map_statement_openers[Statement.Condition], 161 regex_map_statement_closers[Statement.Condition], 162 regex_map_condition[Condition.ElseIf], condition_match); 163 while (else_if_match.found()) 164 { 165 condition_match = else_if_match._close_match; 166 167 string else_if_match_inner = else_if_match._open_match.str(1); 168 auto match_command = RegexObj.match!Condition(else_if_match_inner, 169 regex_map_condition); 170 if (!match_command.found()) 171 { 172 template_engine_throw("parser_error", 173 "unknown if statement: " ~ else_if_match._open_match.str()); 174 } 175 condition_container.children ~= new ElementConditionBranch(else_if_match.inner(), 176 match_command.type(), 177 parse_expression(match_command.str(1))); 178 179 else_if_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(), 180 regex_map_statement_openers[Statement.Condition], 181 regex_map_statement_closers[Statement.Condition], 182 regex_map_condition[Condition.ElseIf], condition_match); 183 } 184 185 MatchClosed else_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(), 186 regex_map_statement_openers[Statement.Condition], 187 regex_map_statement_closers[Statement.Condition], 188 regex_map_condition[Condition.Else], condition_match); 189 if (else_match.found()) 190 { 191 condition_match = else_match._close_match; 192 193 string else_match_inner = else_match._open_match.str(1); 194 auto match_command = RegexObj.match!Condition(else_match_inner, 195 regex_map_condition); 196 if (!match_command.found()) 197 { 198 template_engine_throw("parser_error", 199 "unknown if statement: " ~ else_match._open_match.str()); 200 } 201 //writeln("################### :",match_command.str(1)," else match inner : ",else_match_inner); 202 condition_container.children ~= new ElementConditionBranch(else_match.inner(), 203 match_command.type(), 204 parse_expression(match_command.str(1))); 205 } 206 207 MatchClosed last_if_match = RegexObj.search_closed(input, match_delimiter.pattern(), 208 regex_map_statement_openers[Statement.Condition], 209 regex_map_statement_closers[Statement.Condition], 210 condition_match); 211 //MatchClosed last_if_match = RegexObj.search_closed_on_level(input, match_delimiter.pattern(), regex_map_statement_openers[Statement.Condition], regex_map_statement_closers[Statement.Condition], regex_map_statement_closers[Statement.Condition], condition_match); 212 if (!last_if_match.found()) 213 { 214 writeln("--####- - : ",delimiter_inner); 215 template_engine_throw("parser_error", "misordered if statement"); 216 } 217 218 string last_if_match_inner = last_if_match._open_match.str(1); 219 auto match_command = RegexObj.match!Condition(last_if_match_inner, 220 regex_map_condition); 221 if (!match_command.found()) 222 { 223 template_engine_throw("parser_error", 224 "unknown if statement: " ~ last_if_match._open_match.str()); 225 } 226 if (match_command.type() == Condition.Else) 227 { 228 //writeln("################### 1:",last_if_match.inner()); 229 condition_container.children ~= new ElementConditionBranch(last_if_match.inner(), 230 match_command.type()); 231 } 232 else 233 { 234 //writeln("################### 2:",match_command.str(1)); 235 condition_container.children ~= new ElementConditionBranch(last_if_match.inner(), 236 match_command.type(), 237 parse_expression(match_command.str(1))); 238 } 239 240 current_position = last_if_match.end_position(); 241 result ~= condition_container; 242 break; 243 } 244 case Statement.Include: 245 { 246 string included_filename = path ~ match_statement.str(1); 247 ASTNode included_template = parse_template(included_filename); 248 foreach (element; included_template.parsed_node.children) 249 { 250 result ~= element; 251 } 252 break; 253 } 254 default: 255 { 256 template_engine_throw("parser_error", 257 "unknown statement: " ~ to!string(match_statement.type())); 258 break; 259 } 260 } 261 break; 262 } 263 case Delimiter.Expression: 264 { 265 result ~= parse_expression(delimiter_inner); 266 break; 267 } 268 case Delimiter.Comment: 269 { 270 result ~= new ElementComment(delimiter_inner); 271 break; 272 } 273 default: 274 { 275 template_engine_throw("parser_error", 276 "unknown statement: " ~ to!string(match_delimiter.type())); 277 break; 278 } 279 } 280 281 match_delimiter = RegexObj.search_all(input, current_position); 282 //writeln("-----4------: ",current_position); 283 } 284 if (current_position < input.length) 285 { 286 result ~= new ElementString(input[current_position .. $]); 287 } 288 289 return result; 290 } 291 292 Element parse_tree(Element current_element, string path) 293 { 294 295 if (current_element.inner.length > 0) 296 { 297 //writeln("-----parse_level ------ : ", current_element.inner); 298 current_element.children = parse_level(current_element.inner, path); 299 current_element.inner = string.init; 300 } 301 302 if (current_element.children.length > 0) 303 { 304 for (size_t i = 0; i < current_element.children.length; i++) 305 { 306 //writeln("-----2------"); 307 auto em = current_element.children[i]; 308 //writeln("*******type : ",current_element.type); 309 current_element.children[i] = parse_tree(em, path); 310 } 311 } 312 return current_element; 313 } 314 315 ASTNode parse(string input) 316 { 317 auto parsed = parse_tree(new Element(Type.Main, input), "./"); 318 return new ASTNode(parsed); 319 } 320 321 ASTNode parse_template(string filename) 322 { 323 auto node = ASTCache.node(filename); 324 if (node !is null) 325 return node; 326 327 string input = load_file(filename); 328 string path = dirName(filename); 329 //writeln("----template file path : ",filename); 330 auto parsed = parse_tree(new Element(Type.Main, input), path ~ "/"); 331 auto astnode = new ASTNode(parsed); 332 ASTCache.add(filename, astnode); 333 return astnode; 334 } 335 336 string load_file(string filename) 337 { 338 return cast(string) std.file.read(filename); 339 } 340 }